Skip to content

Conversation

@coratgerl
Copy link
Contributor

@coratgerl coratgerl commented Nov 18, 2025

Pull Request

Issue

Fixes: #8958

  • Add tests
  • Add changes to documentation (guides, repository pages, code comments)
  • Add security check
  • Add new Parse Error codes to Parse JS SDK

Summary by CodeRabbit

  • New Features

    • Centralized sanitized-error helpers added so clients now receive a generic "Permission denied" message.
  • Bug Fixes

    • Internal error details removed from responses while preserving original error codes and behavior.
    • Standardized sanitized error usage for master-key, read-only, auth, and protected-field failures across endpoints.
  • Tests

    • Tests updated to expect "Permission denied" and to verify sanitized server logs include the original detailed reasons.

✏️ Tip: You can customize this high-level summary in your review settings.

@parse-github-assistant
Copy link

I will reformat the title to use the proper commit message syntax.

@parse-github-assistant parse-github-assistant bot changed the title feat: sanitize error for security feat: Sanitize error for security Nov 18, 2025
@parse-github-assistant
Copy link

parse-github-assistant bot commented Nov 18, 2025

🚀 Thanks for opening this pull request!

@coderabbitai
Copy link

coderabbitai bot commented Nov 18, 2025

📝 Walkthrough

Walkthrough

Adds centralized sanitized-error helpers that log detailed server-side messages and return a generic "Permission denied" to clients. Replaces many direct Parse.Error/HTTP error throws across routers, controllers, REST/GraphQL layers, and middleware with sanitized-error constructors; tests updated to expect the generic message and assert sanitized logging.

Changes

Cohort / File(s) Summary
Error helpers
src/Error.js
New helpers createSanitizedError(errorCode, detailedMessage) and createSanitizedHttpError(statusCode, detailedMessage) that log detailed messages and return client-facing "Permission denied" errors.
Core runtime & REST logic
src/SharedRest.js, src/RestQuery.js, src/RestWrite.js, src/rest.js
Replaced direct Parse.Error throws with createSanitizedError for session, class access, protected-field, read-only masterKey, and related permission-denied branches.
Routers
src/Routers/...
src/Routers/ClassesRouter.js, src/Routers/FilesRouter.js, src/Routers/GlobalConfigRouter.js, src/Routers/GraphQLRouter.js, src/Routers/PurgeRouter.js, src/Routers/PushRouter.js, src/Routers/SchemasRouter.js, src/Routers/UsersRouter.js
Permission and master-key error throws replaced by sanitized errors; response codes preserved and client messages generalized to "Permission denied".
Controllers & Middleware
src/Controllers/SchemaController.js, src/middlewares.js
Authentication/permission failures now use sanitized error helpers; HTTP 403 flows return sanitized HTTP errors via createSanitizedHttpError.
GraphQL layer
src/GraphQL/parseGraphQLUtils.js, src/GraphQL/loaders/schemaMutations.js, src/GraphQL/loaders/usersQueries.js
enforceMasterKeyAccess, schema mutations, and session-token user lookups now throw sanitized errors for authorization failures (same codes, generic client message).
Tests — expectations & logging checks
spec/*.spec.js
e.g. spec/AudienceRouter.spec.js, spec/LogsRouter.spec.js, spec/ParseAPI.spec.js, spec/ParseFile.spec.js, spec/ParseGlobalConfig.spec.js, spec/ParseGraphQLServer.spec.js, spec/ParseInstallation.spec.js, spec/ParseQuery.Aggregate.spec.js, spec/ParseUser.spec.js, spec/RestQuery.spec.js, spec/Schema.spec.js, spec/features.spec.js, spec/rest.spec.js, spec/schemas.spec.js, spec/vulnerabilities.spec.js, ...
Tests updated to expect "Permission denied" client messages and to spy on logger.error for sanitized logging (checks for "Sanitized error:" prefix and original message substrings). spec/ParseAPI.spec.js now destructures destroyAllDataPermanently.
Misc / Minor
src/TestUtils.js
Trailing blank-line only; no functional/API changes.
Manifest / package
package.json, manifest_file
Package/manifest entries present in diff (no functional change summarized).

Sequence Diagram(s)

sequenceDiagram
    autonumber
    actor Client
    participant Server
    participant Logger
    note over Client,Server: Authorization failure path (sanitized)
    Client->>Server: Request lacking permission / invalid key
    Server->>Logger: log("Sanitized error: " + detailedMessage)
    alt Respond to client
        Server-->>Client: Error(code) + "Permission denied"
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

  • Pay extra attention to:
    • src/Error.js — logging format, TESTING prefix behavior, and exported API.
    • All replaced error sites (routers, controllers, SharedRest, RestWrite/Query, rest.js, GraphQL loaders) — ensure error codes remain unchanged and no raw Parse.Error leaks remain.
    • src/middlewares.js — HTTP status propagation and response shape for sanitized HTTP errors.
    • Session-related flows in src/Routers/UsersRouter.js and src/rest.js — preserve semantics for session/token errors.
    • Tests that spy on logger.error — verify spy setup/reset locations to avoid flakiness.

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 35.71% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately describes the main objective: sanitizing server internal error details that were leaking to clients in error responses.
Description check ✅ Passed The PR description links to issue #8958, marks the 'Add tests' task as complete, and follows the repository template structure.
Linked Issues check ✅ Passed The PR comprehensively addresses all coding objectives from issue #8958: identifies error messages to generalize, implements server-side detailed logging via createSanitizedError functions, returns generic 'Permission denied' to clients, and preserves error codes.
Out of Scope Changes check ✅ Passed All changes are directly scoped to the linked objective: replacing error throws with sanitized versions, adding test assertions for sanitized logging, and updating expected error messages in tests. No unrelated changes detected.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

📜 Recent review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between dbb4d2a and fa88ffa.

📒 Files selected for processing (1)
  • src/RestQuery.js (4 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/RestQuery.js
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (15)
  • GitHub Check: Node 22
  • GitHub Check: PostgreSQL 18, PostGIS 3.6
  • GitHub Check: Node 18
  • GitHub Check: PostgreSQL 17, PostGIS 3.5
  • GitHub Check: PostgreSQL 15, PostGIS 3.4
  • GitHub Check: PostgreSQL 15, PostGIS 3.5
  • GitHub Check: Node 20
  • GitHub Check: PostgreSQL 16, PostGIS 3.5
  • GitHub Check: PostgreSQL 15, PostGIS 3.3
  • GitHub Check: Redis Cache
  • GitHub Check: MongoDB 6, ReplicaSet
  • GitHub Check: MongoDB 8, ReplicaSet
  • GitHub Check: MongoDB 7, ReplicaSet
  • GitHub Check: Docker Build
  • GitHub Check: Benchmarks

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@parseplatformorg
Copy link
Contributor

parseplatformorg commented Nov 18, 2025

Snyk checks have passed. No issues have been found so far.

Status Scanner Critical High Medium Low Total (0)
Open Source Security 0 0 0 0 0 issues

💻 Catch issues earlier using the plugins for VS Code, JetBrains IDEs, Visual Studio, and Eclipse.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🧹 Nitpick comments (11)
src/SecurityError.js (2)

13-28: Consider extracting the logger selection logic to reduce duplication.

Both createSanitizedError and createSanitizedHttpError contain identical logic for selecting the logger. This violates the DRY principle and makes maintenance harder.

Apply this diff to extract the common logic:

+/**
+ * Selects the appropriate logger from the provided parameter
+ * @param {Object} loggerOrConfig - Optional logger instance or config object
+ * @returns {Object} The selected logger instance
+ */
+function selectLogger(loggerOrConfig) {
+  if (loggerOrConfig) {
+    if (loggerOrConfig.loggerController) {
+      return loggerOrConfig.loggerController;
+    } else if (loggerOrConfig.error) {
+      return loggerOrConfig;
+    }
+  }
+  return defaultLogger;
+}
+
 export function createSanitizedError(errorCode, detailedMessage, loggerOrConfig = null) {
-  let log = defaultLogger;
-  if (loggerOrConfig) {
-    if (loggerOrConfig.loggerController) {
-      log = loggerOrConfig.loggerController;
-    } else if (loggerOrConfig.error) {
-      // It's a logger instance
-      log = loggerOrConfig;
-    }
-  }
+  const log = selectLogger(loggerOrConfig);
 
   // Keep log on server side
   log.error('Security error:', detailedMessage);
 
   return new Parse.Error(errorCode, 'Permission denied');
 }

 export function createSanitizedHttpError(statusCode, detailedMessage, loggerOrConfig = null) {
-  let log = defaultLogger;
-  if (loggerOrConfig) {
-    if (loggerOrConfig.loggerController) {
-      log = loggerOrConfig.loggerController;
-    } else if (loggerOrConfig.error) {
-      log = loggerOrConfig;
-    }
-  }
+  const log = selectLogger(loggerOrConfig);
 
   // Keep log on server side
   log.error('Security error:', detailedMessage);
 
   const error = new Error();
   error.status = statusCode;
   error.message = 'Permission denied';
   return error;
 }

Also applies to: 39-56


1-57: Suggest improved PR title following Angular commit convention.

Based on learnings

The current PR title "feat: sanitize error for security" could be more descriptive and follow the Angular commit convention more precisely. Consider:

feat(security): sanitize error messages to prevent information disclosure

This title:

  • Includes the scope (security)
  • Clearly conveys the security improvement
  • Makes a meaningful changelog entry for developers
src/Routers/FilesRouter.js (1)

8-9: Avoid double‑logging the security error in getHandler

createSanitizedError already logs 'Security error:' with the detailed message, so the explicit log.error('Security error:', detailedError); causes duplicate log entries for the same event.

You can rely on createSanitizedError’s internal logging and drop both the explicit log call and the extra logger argument:

-import { createSanitizedError } from '../SecurityError';
-import defaultLogger from '../logger';
+import { createSanitizedError } from '../SecurityError';
@@
     if (!config) {
       res.status(403);
-      const detailedError = 'Invalid application ID.';
-      const log = defaultLogger;
-      log.error('Security error:', detailedError);
-      const err = createSanitizedError(Parse.Error.OPERATION_FORBIDDEN, detailedError, log);
+      const detailedError = 'Invalid application ID.';
+      const err = createSanitizedError(Parse.Error.OPERATION_FORBIDDEN, detailedError);
       res.json({ code: err.code, error: err.message });
       return;
     }

This keeps behavior the same (code + sanitized message) while logging only once.

Also applies to: 48-52

spec/ParseGraphQLServer.spec.js (1)

47-61: PR title: suggest Angular-style scoped variant

To make the changelog entry more informative and follow the Angular convention from this repo, consider updating the PR title to something like:

feat(security): sanitize permission error messages

This reflects the security impact and the main behavior change more clearly.

src/RestQuery.js (1)

432-435: Use config-aware logger consistently when sanitizing errors in RestQuery

denyProtectedFields correctly derives log from this.config.loggerController || defaultLogger, but validateClientClassCreation currently calls createSanitizedError without passing any logger/config, so it always falls back to the global default.

To keep logging consistent and app-scoped, consider:

-        if (hasClass !== true) {
-          throw createSanitizedError(
-            Parse.Error.OPERATION_FORBIDDEN,
-            'This user is not allowed to access ' + 'non-existent class: ' + this.className
-          );
-        }
+        if (hasClass !== true) {
+          const detailedError =
+            'This user is not allowed to access non-existent class: ' + this.className;
+          const log = (this.config && this.config.loggerController) || defaultLogger;
+          throw createSanitizedError(
+            Parse.Error.OPERATION_FORBIDDEN,
+            detailedError,
+            log
+          );
+        }

Also applies to: 811-816

src/triggers.js (1)

4-6: Validator now distinguishes security vs generic validation errors

The updated maybeRunValidator logic:

  • Correctly treats "Master key is required" and "Please login to continue" failures as security-sensitive, logging the detailed reason via defaultLogger and returning a generic VALIDATION_ERROR with message 'Permission denied'.
  • Preserves the existing 'Validation failed.' behavior for non-security validation problems.

If you want to further centralize sanitization, you could consider reusing the shared helper instead of hand-rolling:

-        if (isSecurityError) {
-          // Log detailed error server-side and sanitize for client
-          defaultLogger.error('Security validation error:', errorMessage);
-          error = resolveError(e, {
-            code: Parse.Error.VALIDATION_ERROR,
-            message: 'Permission denied',
-          });
-        } else {
+        if (isSecurityError) {
+          defaultLogger.error('Security validation error:', errorMessage);
+          error = resolveError(e, {
+            code: Parse.Error.VALIDATION_ERROR,
+            message: 'Permission denied',
+          });
+        } else {

(or swap in createSanitizedError if you decide to standardize on that helper for all security errors.)

Also applies to: 696-735

src/RestWrite.js (1)

20-22: RestWrite sanitization is sound; consider minor logger and read-only consistency tweaks

The new sanitized error paths in RestWrite are functionally correct:

  • validateClientClassCreation and the unauthenticated _User update guard now log detailed reasons via (this.config && this.config.loggerController) || defaultLogger and expose only a generic permission error to clients.
  • checkRestrictedFields correctly blocks client-side updates to emailVerified with OPERATION_FORBIDDEN.

A few small follow-ups would make this file more consistent with the rest of the PR:

  1. Use the config-aware logger in checkRestrictedFields

Right now it relies on createSanitizedError’s internal default logger. To align with the other call sites:

   if (!this.auth.isMaintenance && !this.auth.isMaster && 'emailVerified' in this.data) {
-    throw createSanitizedError(Parse.Error.OPERATION_FORBIDDEN, 'Clients aren\'t allowed to manually update email verification.');
+    const log = (this.config && this.config.loggerController) || defaultLogger;
+    throw createSanitizedError(
+      Parse.Error.OPERATION_FORBIDDEN,
+      "Clients aren't allowed to manually update email verification.",
+      log
+    );
   }
  1. Deduplicate logger imports

You currently import the same default export twice:

import logger from './logger';
import defaultLogger from './logger';

You can keep just one default import and reuse it (e.g. stick with logger and drop defaultLogger, or vice versa), which avoids confusion and potential lint warnings.

  1. Optional: sanitize the read-only masterKey constructor guard

The constructor still throws a raw Parse.Error mentioning readOnlyMasterKey. Given that SharedRest.enforceRoleSecurity should block external read-only writes first, this is mostly an internal edge case, but you may still want to route it through createSanitizedError for consistency with other read-only enforcement paths.

Also applies to: 191-216, 662-669, 1456-1463

src/SharedRest.js (3)

1-15: Configurable logger integration and signature change look correct; consider minor DRY refactor

  • Importing createSanitizedError / defaultLogger and extending enforceRoleSecurity to accept config = null is backward compatible and aligns with the new sanitization helper.
  • To avoid repeating config || defaultLogger in each branch, consider computing it once at the top of the function, e.g. const loggerOrConfig = config || defaultLogger;, and reuse it below.

17-20: Sanitized errors across all forbidden paths align with the security objective; optional helper could reduce duplication

  • Each forbidden-operation path now builds a clear detailedError, selects loggerOrConfig, and throws createSanitizedError(Parse.Error.OPERATION_FORBIDDEN, …, loggerOrConfig), which should log server-side and return a generic “Permission denied” to clients—this matches the goal of not leaking details while preserving the error code.
  • The three branches share the same pattern; you could optionally factor a tiny local helper (or reuse a precomputed loggerOrConfig) to reduce duplication and keep future changes to the sanitization behavior in one place.

Also applies to: 29-31, 36-38


1-44: PR-level: title suggestion and documentation note for this security behavior change

  • Given this is a security-focused behavioral change that developers should notice in the changelog, consider renaming the PR to an Angular-style title such as:
    feat(security): sanitize permission errors
  • It may also be worth adding a short note to the security/docs/README explaining that certain authorization errors are now always surfaced as generic “Permission denied” while details are only logged server-side, so integrators know not to rely on error message text.

Based on learnings

src/Routers/SchemasRouter.js (1)

8-9: Sanitized error utilities imported correctly; minor DRY opportunity and PR title suggestion

Imports for createSanitizedError and defaultLogger look good and match the shared helper pattern. To avoid repeating the same logger expression in multiple handlers, you could optionally introduce a small helper (e.g. const log = req.config?.loggerController || defaultLogger;) or pass req.config directly into createSanitizedError and let it resolve the logger internally, for slightly cleaner code.

Side note for the PR itself: an Angular-style title that would read well in the changelog could be:

fix(security): sanitize read-only schema error messages

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 50650a3 and a310696.

📒 Files selected for processing (34)
  • spec/AudienceRouter.spec.js (5 hunks)
  • spec/LogsRouter.spec.js (1 hunks)
  • spec/ParseAPI.spec.js (1 hunks)
  • spec/ParseFile.spec.js (3 hunks)
  • spec/ParseGlobalConfig.spec.js (1 hunks)
  • spec/ParseGraphQLServer.spec.js (7 hunks)
  • spec/ParseInstallation.spec.js (1 hunks)
  • spec/ParseQuery.Aggregate.spec.js (1 hunks)
  • spec/ParseUser.spec.js (3 hunks)
  • spec/RestQuery.spec.js (2 hunks)
  • spec/Schema.spec.js (5 hunks)
  • spec/features.spec.js (1 hunks)
  • spec/rest.spec.js (11 hunks)
  • spec/schemas.spec.js (10 hunks)
  • spec/vulnerabilities.spec.js (1 hunks)
  • src/Controllers/SchemaController.js (4 hunks)
  • src/GraphQL/loaders/schemaMutations.js (4 hunks)
  • src/GraphQL/loaders/schemaQueries.js (2 hunks)
  • src/GraphQL/parseGraphQLUtils.js (1 hunks)
  • src/RestQuery.js (5 hunks)
  • src/RestWrite.js (4 hunks)
  • src/Routers/ClassesRouter.js (2 hunks)
  • src/Routers/FilesRouter.js (2 hunks)
  • src/Routers/GlobalConfigRouter.js (2 hunks)
  • src/Routers/GraphQLRouter.js (2 hunks)
  • src/Routers/PurgeRouter.js (1 hunks)
  • src/Routers/PushRouter.js (2 hunks)
  • src/Routers/SchemasRouter.js (4 hunks)
  • src/Routers/UsersRouter.js (2 hunks)
  • src/SecurityError.js (1 hunks)
  • src/SharedRest.js (3 hunks)
  • src/middlewares.js (2 hunks)
  • src/rest.js (6 hunks)
  • src/triggers.js (2 hunks)
🧰 Additional context used
🧠 Learnings (10)
📓 Common learnings
Learnt from: mtrezza
Repo: parse-community/parse-server PR: 0
File: :0-0
Timestamp: 2025-11-17T15:02:48.786Z
Learning: For Parse Server PRs, always suggest an Angular commit convention PR title that would make a meaningful changelog entry for developers. Update the PR title suggestion on every commit. The format should be: type(scope): description. Common types include feat, fix, perf, refactor, docs, test, chore. The scope should identify the subsystem (e.g., graphql, rest, push, security). The description should be action-oriented and clearly convey the change's impact to developers.
Learnt from: mtrezza
Repo: parse-community/parse-server PR: 0
File: :0-0
Timestamp: 2025-11-08T13:46:04.940Z
Learning: When reviewing Parse Server PRs that add new features, always check whether the feature is documented in the README.md file, though for new Parse Server options this is optional rather than required.
Learnt from: mtrezza
Repo: parse-community/parse-server PR: 0
File: :0-0
Timestamp: 2025-11-17T15:02:24.824Z
Learning: For Parse Server PRs, always suggest an Angular-style PR title that would make a meaningful changelog entry for developers. Update the PR title suggestion with every new commit to the PR.
📚 Learning: 2025-05-09T09:59:06.289Z
Learnt from: mtrezza
Repo: parse-community/parse-server PR: 9445
File: spec/ParseLiveQuery.spec.js:1340-1375
Timestamp: 2025-05-09T09:59:06.289Z
Learning: Tests in the parse-server repository should use promise-based approaches rather than callback patterns with `done()`. Use a pattern where a Promise is created that resolves when the event occurs, then await that promise.

Applied to files:

  • spec/ParseInstallation.spec.js
  • spec/Schema.spec.js
  • spec/schemas.spec.js
  • spec/rest.spec.js
  • spec/ParseUser.spec.js
  • spec/vulnerabilities.spec.js
  • spec/RestQuery.spec.js
  • spec/ParseGraphQLServer.spec.js
  • src/triggers.js
📚 Learning: 2025-05-09T09:59:06.289Z
Learnt from: mtrezza
Repo: parse-community/parse-server PR: 9445
File: spec/ParseLiveQuery.spec.js:1340-1375
Timestamp: 2025-05-09T09:59:06.289Z
Learning: New tests in the parse-server repository should use async/await with promise-based patterns rather than callback patterns with `done()`. The preferred pattern is to create a Promise that resolves when an expected event occurs, then await that Promise.

Applied to files:

  • spec/Schema.spec.js
  • spec/schemas.spec.js
  • spec/rest.spec.js
  • spec/ParseUser.spec.js
  • spec/vulnerabilities.spec.js
  • spec/RestQuery.spec.js
  • spec/ParseGraphQLServer.spec.js
  • src/triggers.js
📚 Learning: 2025-05-04T20:41:05.147Z
Learnt from: mtrezza
Repo: parse-community/parse-server PR: 9445
File: spec/ParseLiveQuery.spec.js:1312-1338
Timestamp: 2025-05-04T20:41:05.147Z
Learning: New tests in the parse-server repository should use async/await with promise-based patterns rather than callback patterns with `done()`.

Applied to files:

  • spec/Schema.spec.js
  • spec/rest.spec.js
  • spec/ParseUser.spec.js
  • spec/vulnerabilities.spec.js
  • spec/RestQuery.spec.js
  • spec/ParseGraphQLServer.spec.js
  • src/triggers.js
📚 Learning: 2025-11-08T13:46:04.940Z
Learnt from: mtrezza
Repo: parse-community/parse-server PR: 0
File: :0-0
Timestamp: 2025-11-08T13:46:04.940Z
Learning: For new Parse Server options, verify that the option is documented in src/Options/index.js and that npm run definitions has been executed to reflect changes in src/Options/docs.js and src/Options/Definitions.js. README.md documentation is a bonus but not required for new options.

Applied to files:

  • src/SharedRest.js
📚 Learning: 2025-09-21T15:43:32.265Z
Learnt from: mtrezza
Repo: parse-community/parse-server PR: 9858
File: src/GraphQL/ParseGraphQLServer.js:176-178
Timestamp: 2025-09-21T15:43:32.265Z
Learning: The GraphQL playground feature in ParseGraphQLServer.js (applyPlayground method) is intended for development environments only, which is why it includes the master key in client-side headers.

Applied to files:

  • src/GraphQL/loaders/schemaMutations.js
  • src/Routers/GraphQLRouter.js
  • src/GraphQL/parseGraphQLUtils.js
  • spec/ParseGraphQLServer.spec.js
📚 Learning: 2025-04-30T19:31:35.344Z
Learnt from: RahulLanjewar93
Repo: parse-community/parse-server PR: 9744
File: spec/ParseLiveQuery.spec.js:0-0
Timestamp: 2025-04-30T19:31:35.344Z
Learning: In the Parse Server codebase, the functions in QueryTools.js are typically tested through end-to-end behavior tests rather than direct unit tests, even though the functions are exported from the module.

Applied to files:

  • spec/rest.spec.js
  • src/Routers/ClassesRouter.js
  • spec/RestQuery.spec.js
📚 Learning: 2025-08-27T12:33:06.237Z
Learnt from: EmpiDev
Repo: parse-community/parse-server PR: 9770
File: src/triggers.js:467-477
Timestamp: 2025-08-27T12:33:06.237Z
Learning: In the Parse Server codebase, maybeRunAfterFindTrigger is called in production with Parse.Query objects constructed via withJSON(), so the plain object query handling bug only affects tests, not production code paths.

Applied to files:

  • spec/rest.spec.js
  • src/RestQuery.js
📚 Learning: 2025-08-26T14:06:31.853Z
Learnt from: EmpiDev
Repo: parse-community/parse-server PR: 9770
File: spec/CloudCode.spec.js:446-469
Timestamp: 2025-08-26T14:06:31.853Z
Learning: In the Parse Server codebase, when handling query objects in maybeRunAfterFindTrigger, objects without a where property that contain options like limit/skip should be treated as query JSON with an empty where clause using the spread pattern { where: {}, ...query }, not nested as { where: query }.

Applied to files:

  • src/RestQuery.js
📚 Learning: 2025-11-17T15:02:48.786Z
Learnt from: mtrezza
Repo: parse-community/parse-server PR: 0
File: :0-0
Timestamp: 2025-11-17T15:02:48.786Z
Learning: For Parse Server PRs, always suggest an Angular commit convention PR title that would make a meaningful changelog entry for developers. Update the PR title suggestion on every commit. The format should be: type(scope): description. Common types include feat, fix, perf, refactor, docs, test, chore. The scope should identify the subsystem (e.g., graphql, rest, push, security). The description should be action-oriented and clearly convey the change's impact to developers.

Applied to files:

  • spec/ParseGraphQLServer.spec.js
🧬 Code graph analysis (24)
src/SharedRest.js (3)
src/RestQuery.js (6)
  • require (7-7)
  • require (9-9)
  • require (10-10)
  • defaultLogger (11-11)
  • detailedError (125-125)
  • detailedError (811-811)
src/rest.js (4)
  • require (15-15)
  • require (329-329)
  • defaultLogger (330-330)
  • detailedError (331-331)
src/SecurityError.js (1)
  • createSanitizedError (13-28)
spec/schemas.spec.js (1)
src/Routers/SchemasRouter.js (2)
  • response (51-56)
  • response (64-70)
src/GraphQL/loaders/schemaQueries.js (1)
src/GraphQL/parseGraphQLUtils.js (1)
  • enforceMasterKeyAccess (6-15)
src/GraphQL/loaders/schemaMutations.js (4)
src/GraphQL/parseGraphQLUtils.js (2)
  • enforceMasterKeyAccess (6-15)
  • loggerOrConfig (8-8)
src/middlewares.js (4)
  • enforceMasterKeyAccess (503-512)
  • config (208-208)
  • config (644-644)
  • config (646-646)
src/SharedRest.js (5)
  • loggerOrConfig (18-18)
  • loggerOrConfig (30-30)
  • loggerOrConfig (37-37)
  • defaultLogger (11-11)
  • Parse (1-1)
src/SecurityError.js (1)
  • createSanitizedError (13-28)
src/Routers/GraphQLRouter.js (2)
src/RestQuery.js (3)
  • log (126-126)
  • log (812-812)
  • defaultLogger (11-11)
src/SecurityError.js (3)
  • log (14-14)
  • log (40-40)
  • createSanitizedError (13-28)
src/SecurityError.js (6)
src/SharedRest.js (4)
  • loggerOrConfig (18-18)
  • loggerOrConfig (30-30)
  • loggerOrConfig (37-37)
  • defaultLogger (11-11)
src/RestQuery.js (3)
  • log (126-126)
  • log (812-812)
  • defaultLogger (11-11)
src/RestWrite.js (2)
  • log (205-205)
  • log (1458-1458)
src/Routers/SchemasRouter.js (3)
  • log (77-77)
  • log (101-101)
  • log (118-118)
src/middlewares.js (8)
  • log (237-237)
  • log (247-247)
  • log (464-464)
  • log (505-505)
  • log (516-516)
  • log (539-539)
  • error (252-252)
  • error (506-506)
src/rest.js (2)
  • log (332-332)
  • defaultLogger (330-330)
src/Routers/FilesRouter.js (3)
src/RestQuery.js (3)
  • log (126-126)
  • log (812-812)
  • defaultLogger (11-11)
src/SharedRest.js (2)
  • defaultLogger (11-11)
  • Parse (1-1)
src/SecurityError.js (3)
  • log (14-14)
  • log (40-40)
  • createSanitizedError (13-28)
src/middlewares.js (1)
src/SecurityError.js (4)
  • log (14-14)
  • log (40-40)
  • error (52-52)
  • createSanitizedHttpError (39-56)
src/Routers/ClassesRouter.js (1)
src/SecurityError.js (2)
  • log (14-14)
  • createSanitizedError (13-28)
spec/features.spec.js (1)
src/middlewares.js (2)
  • error (252-252)
  • error (506-506)
src/RestWrite.js (5)
src/RestQuery.js (5)
  • detailedError (125-125)
  • detailedError (811-811)
  • log (126-126)
  • log (812-812)
  • defaultLogger (11-11)
src/SharedRest.js (5)
  • detailedError (17-17)
  • detailedError (29-29)
  • detailedError (36-36)
  • defaultLogger (11-11)
  • Parse (1-1)
src/rest.js (3)
  • detailedError (331-331)
  • log (332-332)
  • defaultLogger (330-330)
src/SecurityError.js (3)
  • log (14-14)
  • log (40-40)
  • createSanitizedError (13-28)
src/middlewares.js (6)
  • log (237-237)
  • log (247-247)
  • log (464-464)
  • log (505-505)
  • log (516-516)
  • log (539-539)
src/Controllers/SchemaController.js (1)
src/SecurityError.js (1)
  • createSanitizedError (13-28)
src/GraphQL/parseGraphQLUtils.js (3)
src/middlewares.js (4)
  • enforceMasterKeyAccess (503-512)
  • config (208-208)
  • config (644-644)
  • config (646-646)
src/SharedRest.js (5)
  • loggerOrConfig (18-18)
  • loggerOrConfig (30-30)
  • loggerOrConfig (37-37)
  • defaultLogger (11-11)
  • Parse (1-1)
src/SecurityError.js (1)
  • createSanitizedError (13-28)
src/RestQuery.js (4)
src/SharedRest.js (6)
  • require (10-10)
  • defaultLogger (11-11)
  • detailedError (17-17)
  • detailedError (29-29)
  • detailedError (36-36)
  • Parse (1-1)
src/rest.js (5)
  • require (15-15)
  • require (329-329)
  • defaultLogger (330-330)
  • detailedError (331-331)
  • log (332-332)
src/middlewares.js (9)
  • config (208-208)
  • config (644-644)
  • config (646-646)
  • log (237-237)
  • log (247-247)
  • log (464-464)
  • log (505-505)
  • log (516-516)
  • log (539-539)
src/SecurityError.js (3)
  • log (14-14)
  • log (40-40)
  • createSanitizedError (13-28)
src/Routers/UsersRouter.js (7)
src/RestQuery.js (3)
  • log (126-126)
  • log (812-812)
  • defaultLogger (11-11)
src/RestWrite.js (2)
  • log (205-205)
  • log (1458-1458)
src/Routers/SchemasRouter.js (3)
  • log (77-77)
  • log (101-101)
  • log (118-118)
src/SecurityError.js (2)
  • log (14-14)
  • createSanitizedError (13-28)
src/middlewares.js (6)
  • log (237-237)
  • log (247-247)
  • log (464-464)
  • log (505-505)
  • log (516-516)
  • log (539-539)
src/rest.js (2)
  • log (332-332)
  • defaultLogger (330-330)
src/SharedRest.js (2)
  • defaultLogger (11-11)
  • Parse (1-1)
spec/AudienceRouter.spec.js (1)
src/middlewares.js (2)
  • error (252-252)
  • error (506-506)
spec/ParseAPI.spec.js (1)
src/Routers/SchemasRouter.js (2)
  • response (51-56)
  • response (64-70)
src/rest.js (3)
src/RestQuery.js (9)
  • className (1001-1001)
  • require (7-7)
  • require (9-9)
  • require (10-10)
  • defaultLogger (11-11)
  • detailedError (125-125)
  • detailedError (811-811)
  • log (126-126)
  • log (812-812)
src/SecurityError.js (4)
  • error (52-52)
  • log (14-14)
  • log (40-40)
  • createSanitizedError (13-28)
src/SharedRest.js (6)
  • Parse (1-1)
  • require (10-10)
  • defaultLogger (11-11)
  • detailedError (17-17)
  • detailedError (29-29)
  • detailedError (36-36)
src/Routers/PurgeRouter.js (1)
src/SecurityError.js (3)
  • log (14-14)
  • log (40-40)
  • createSanitizedError (13-28)
src/Routers/GlobalConfigRouter.js (2)
src/Routers/SchemasRouter.js (3)
  • log (77-77)
  • log (101-101)
  • log (118-118)
src/SecurityError.js (3)
  • log (14-14)
  • log (40-40)
  • createSanitizedError (13-28)
src/triggers.js (5)
src/Auth.js (2)
  • e (582-585)
  • Parse (1-1)
src/SecurityError.js (1)
  • error (52-52)
src/middlewares.js (2)
  • error (252-252)
  • error (506-506)
src/RestQuery.js (1)
  • defaultLogger (11-11)
src/SharedRest.js (2)
  • defaultLogger (11-11)
  • Parse (1-1)
spec/ParseQuery.Aggregate.spec.js (1)
src/middlewares.js (2)
  • error (252-252)
  • error (506-506)
src/Routers/SchemasRouter.js (4)
src/RestQuery.js (3)
  • log (126-126)
  • log (812-812)
  • defaultLogger (11-11)
src/SecurityError.js (3)
  • log (14-14)
  • log (40-40)
  • createSanitizedError (13-28)
src/middlewares.js (6)
  • log (237-237)
  • log (247-247)
  • log (464-464)
  • log (505-505)
  • log (516-516)
  • log (539-539)
src/rest.js (2)
  • log (332-332)
  • defaultLogger (330-330)
src/Routers/PushRouter.js (2)
src/RestQuery.js (3)
  • log (126-126)
  • log (812-812)
  • defaultLogger (11-11)
src/SecurityError.js (3)
  • log (14-14)
  • log (40-40)
  • createSanitizedError (13-28)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (16)
  • GitHub Check: Node 18
  • GitHub Check: Node 20
  • GitHub Check: Node 22
  • GitHub Check: MongoDB 8, ReplicaSet
  • GitHub Check: MongoDB 7, ReplicaSet
  • GitHub Check: Redis Cache
  • GitHub Check: PostgreSQL 17, PostGIS 3.5
  • GitHub Check: PostgreSQL 15, PostGIS 3.5
  • GitHub Check: PostgreSQL 15, PostGIS 3.4
  • GitHub Check: PostgreSQL 18, PostGIS 3.6
  • GitHub Check: MongoDB 6, ReplicaSet
  • GitHub Check: PostgreSQL 16, PostGIS 3.5
  • GitHub Check: PostgreSQL 15, PostGIS 3.3
  • GitHub Check: Code Analysis (javascript)
  • GitHub Check: Docker Build
  • GitHub Check: Benchmarks
🔇 Additional comments (24)
spec/LogsRouter.spec.js (1)

64-64: LGTM! Test expectation correctly updated.

The error message expectation has been updated to align with the sanitized error handling. This test correctly verifies that unauthorized requests without a master key now receive the generic "Permission denied" message.

spec/ParseGlobalConfig.spec.js (1)

236-236: LGTM! Consistent with sanitized error handling.

The test correctly expects "Permission denied" when attempting to update global config without the master key.

spec/vulnerabilities.spec.js (1)

18-18: LGTM! Error message appropriately generalized for security vulnerability.

The test for GHSA-8xq9-g7ch-35hg (custom object ID privilege escalation vulnerability) now correctly expects "Permission denied" instead of "Invalid object ID." This generic message prevents leaking implementation details about the security check while maintaining the OPERATION_FORBIDDEN error code.

spec/features.spec.js (1)

35-35: LGTM! Test correctly updated for serverInfo endpoint.

The test appropriately expects "Permission denied" when requesting serverInfo without the master key.

spec/ParseQuery.Aggregate.spec.js (1)

80-80: LGTM! Aggregate query error message correctly sanitized.

The test correctly expects "Permission denied" for aggregate queries without master key authentication.

spec/ParseAPI.spec.js (1)

1727-1727: LGTM! Purge operation error correctly updated.

The test appropriately expects "Permission denied" when attempting to purge objects without the master key.

spec/Schema.spec.js (1)

286-286: LGTM! All schema permission tests correctly updated.

All test expectations have been consistently updated to "Permission denied" for:

  • Count operations without permission (line 286)
  • Required auth scenarios for find, create, get, and find operations (lines 1465, 1564, 1652, 1697)

These changes align with the sanitized error handling across permission-denied scenarios.

Also applies to: 1465-1465, 1564-1564, 1652-1652, 1697-1697

src/Routers/PurgeRouter.js (1)

4-5: Sanitized error for read‑only master key purge looks correct

Using createSanitizedError with (req.config && req.config.loggerController) || defaultLogger matches the new security pattern and preserves the original error code while hiding details from clients.

Also applies to: 10-15

src/Routers/UsersRouter.js (1)

19-21: Sanitized error for /loginAs without master key is consistent

handleLogInAs now routes the missing-master-key case through createSanitizedError with (req.config && req.config.loggerController) || defaultLogger, which matches the new security model and keeps the error code stable while generalizing the client‑facing message.

Also applies to: 336-344

src/Routers/GraphQLRouter.js (1)

4-5: GraphQL config updates correctly use sanitized error for read‑only master key

For read‑only master keys, updateGraphQLConfig now throws a createSanitizedError with Parse.Error.OPERATION_FORBIDDEN and a logger derived from req.config.loggerController or defaultLogger, which aligns with the new permission‑denied semantics.

Also applies to: 17-24

spec/rest.spec.js (1)

316-331: Tests correctly updated to expect sanitized 'Permission denied' messages

The affected tests still assert the correct Parse error codes (e.g. OPERATION_FORBIDDEN) but now expect the generic 'Permission denied' message for:

  • Client‑class‑creation being disabled.
  • Pointer reads into master‑key‑only classes (_PushStatus, _GlobalConfig).
  • All read-only masterKey write operations (REST, schemas, config, push).

This matches the new sanitized error behavior while keeping codes stable for clients.

Also applies to: 772-786, 788-802, 945-989, 1024-1138

spec/RestQuery.spec.js (1)

157-172: RestQuery tests align with sanitized forbidden errors

Both the disabled‑client‑class and protected‑field tests now assert the same error codes as before but with a generic 'Permission denied' message, which is consistent with the new sanitization logic.

Also applies to: 216-248

src/Routers/ClassesRouter.js (1)

6-8: Sanitized error for invalid _User objectId (role: prefix) is appropriate

handleCreate now rejects _User objects whose objectId starts with 'role:' using createSanitizedError and (req.config && req.config.loggerController) || defaultLogger, preserving the forbidden semantics while hiding the reason from clients.

Also applies to: 110-119

spec/ParseUser.spec.js (1)

2634-2665: User/session tests correctly updated for generic permission errors

The affected tests now:

  • Keep their original error codes (e.g. 209 for invalid/missing session token, OPERATION_FORBIDDEN for forbidden updates and /loginAs without master key), and
  • Expect 'Permission denied' instead of more specific messages like 'Session token required.', "Clients aren't allowed to manually update email verification.", or 'master key is required'.

This is consistent with the new sanitized error strategy and encourages clients to key off error codes rather than message text.

Also applies to: 3351-3384, 4374-4397

spec/ParseFile.spec.js (2)

159-159: LGTM! Security improvement correctly tested.

The test expectations now correctly validate that generic "Permission denied" messages are returned for both missing and incorrect master key scenarios, preventing information disclosure about authentication requirements.

Also applies to: 172-172


763-763: LGTM! Error code preserved while message sanitized.

The test correctly validates that error code 119 is maintained while the message is generalized from "Invalid application ID." to "Permission denied", preventing information disclosure about application IDs while preserving API contract.

spec/ParseGraphQLServer.spec.js (2)

3490-3505: Sanitized forbidden-error expectations are consistent with the new security model

All updated assertions that now expect Parse.Error.OPERATION_FORBIDDEN with the message 'Permission denied' for class schema operations (create, update, delete, get, list) align with the PR goal of hiding authorization details while keeping codes stable. The tests remain precise and should fail if any endpoint accidentally reintroduces a more verbose error.

Also applies to: 3841-3875, 4066-4100, 4126-4142, 4144-4160


6058-6125: Updated permission checks for object creation correctly target sanitized messages

The createObject permission tests now reject with jasmine.stringMatching('Permission denied') instead of a more detailed unauthorized message, which matches the sanitized error behavior while remaining resilient to small wording changes around that phrase. Looks good.

src/GraphQL/parseGraphQLUtils.js (1)

3-13: Config-aware sanitized master-key enforcement looks correct

Using createSanitizedError with loggerOrConfig = config || defaultLogger ensures clients only see a generic permission error while detailed messages are logged via the app logger when available. The updated enforceMasterKeyAccess(auth, config) signature is consistent with this pattern.

src/RestQuery.js (1)

10-11: RestQuery master/session security sanitization is consistent

  • Passing config into enforceRoleSecurity and using createSanitizedError for invalid session tokens ensures _Session access errors are logged with full detail while clients see a generic permission failure.
  • Use of (this.config && this.config.loggerController) || defaultLogger for the session-token path matches the broader pattern introduced in this PR.

Also applies to: 56-56, 122-132

src/Controllers/SchemaController.js (1)

23-24: CLP permission-denial sanitization preserves codes while hiding detail

The new createSanitizedError usage in SchemaController.validatePermission correctly:

  • Keeps OBJECT_NOT_FOUND for unauthenticated access and OPERATION_FORBIDDEN for CLP denials.
  • Logs detailed reasons via defaultLogger while only exposing a generic permission error to clients.

This is consistent with the PR’s security objective and should not break code-based error handling.

Also applies to: 1408-1413, 1426-1428, 1447-1448

src/Routers/SchemasRouter.js (3)

76-82: Read‑only create‑schema error is now properly sanitized and logged

The switch to createSanitizedError preserves the OPERATION_FORBIDDEN code, logs the detailed read‑only master key message via the resolved logger, and returns the generic client message. This matches the intended security behavior.


100-106: Consistent sanitized handling for schema updates

The update path mirrors the create handler: same code, same detailed message confined to logs, and sanitized client error. The consistency across schema mutations is good.


118-123: Delete‑schema read‑only handling aligned with other mutations

The delete handler now follows the same sanitized error pattern as create/update while keeping the forbidden error code. This keeps behavior uniform across all schema‑modifying operations.

@codecov
Copy link

codecov bot commented Nov 18, 2025

Codecov Report

❌ Patch coverage is 82.08955% with 12 lines in your changes missing coverage. Please review.
✅ Project coverage is 93.08%. Comparing base (38c9d2e) to head (75e7148).
⚠️ Report is 2 commits behind head on alpha.

Files with missing lines Patch % Lines
src/GraphQL/loaders/schemaMutations.js 25.00% 3 Missing ⚠️
src/Error.js 83.33% 2 Missing ⚠️
src/Routers/UsersRouter.js 50.00% 2 Missing ⚠️
src/Controllers/SchemaController.js 80.00% 1 Missing ⚠️
src/GraphQL/loaders/usersQueries.js 66.66% 1 Missing ⚠️
src/Routers/GraphQLRouter.js 50.00% 1 Missing ⚠️
src/Routers/PurgeRouter.js 50.00% 1 Missing ⚠️
src/rest.js 66.66% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##            alpha    #9937      +/-   ##
==========================================
- Coverage   93.08%   93.08%   -0.01%     
==========================================
  Files         187      188       +1     
  Lines       15275    15298      +23     
  Branches      177      177              
==========================================
+ Hits        14219    14240      +21     
- Misses       1044     1046       +2     
  Partials       12       12              

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (4)
src/SecurityError.js (3)

12-21: Make logger resolution more defensive and DRY.

Both helpers duplicate the same logger resolution logic and treat any truthy loggerOrConfig.error as a logger, even if it’s not a function. That’s fine for the expected config / logger inputs, but it’s brittle for accidental misuse.

Consider extracting a small resolveLogger(loggerOrConfig) helper that:

  • Returns loggerOrConfig.loggerController when present.
  • Otherwise returns loggerOrConfig only if typeof loggerOrConfig.error === 'function'.
  • Falls back to defaultLogger in all other cases.

That removes duplication and avoids ever ending up with an object that doesn’t actually implement error().

Also applies to: 38-46


23-27: Optional: enrich server‑side logs with error code / status as metadata.

Right now the log entry only includes the string message; the returned error carries the code/status, but that information is not in the log call itself. For security auditing and debugging it can be useful to log the numeric errorCode / statusCode as well (e.g. as an additional argument or metadata object), while still keeping the client message sanitized.

This is not required for correctness, but worth considering for more informative security logs across the codebase.

Also applies to: 48-54


1-55: PR‑level: align title with Angular convention and consider documenting the new error behavior.

Per the repo’s conventions, you may want to adjust the PR title to an Angular‑style variant that’s clearer in the changelog, for example:

  • feat(security): sanitize permission error responses

Also, since this changes client‑visible error messages (even though codes stay the same), consider a short note in the README or upgrade notes explaining that certain security/permission errors now always return "Permission denied" while detailed reasons are only available in server logs.

src/SharedRest.js (1)

9-10: Access checks look correct; consider centralizing logger resolution.

The refactor keeps the existing access rules intact while routing all denials through createSanitizedError, which is exactly what we want for hiding detailed messages from clients and logging them server‑side. The new config parameter is optional, so existing callers remain valid and fall back to defaultLogger.

You could optionally compute const loggerOrConfig = config || defaultLogger; once near the top of enforceRoleSecurity and reuse it in all three branches to avoid repetition, but that’s purely cosmetic and not required.

Also applies to: 13-19, 28-31, 35-37

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between a310696 and a413efd.

📒 Files selected for processing (3)
  • src/SecurityError.js (1 hunks)
  • src/SharedRest.js (2 hunks)
  • src/triggers.js (1 hunks)
🧰 Additional context used
🧠 Learnings (1)
📓 Common learnings
Learnt from: mtrezza
Repo: parse-community/parse-server PR: 0
File: :0-0
Timestamp: 2025-11-17T15:02:48.786Z
Learning: For Parse Server PRs, always suggest an Angular commit convention PR title that would make a meaningful changelog entry for developers. Update the PR title suggestion on every commit. The format should be: type(scope): description. Common types include feat, fix, perf, refactor, docs, test, chore. The scope should identify the subsystem (e.g., graphql, rest, push, security). The description should be action-oriented and clearly convey the change's impact to developers.
Learnt from: mtrezza
Repo: parse-community/parse-server PR: 0
File: :0-0
Timestamp: 2025-11-08T13:46:04.940Z
Learning: When reviewing Parse Server PRs that add new features, always check whether the feature is documented in the README.md file, though for new Parse Server options this is optional rather than required.
Learnt from: mtrezza
Repo: parse-community/parse-server PR: 0
File: :0-0
Timestamp: 2025-11-17T15:02:24.824Z
Learning: For Parse Server PRs, always suggest an Angular-style PR title that would make a meaningful changelog entry for developers. Update the PR title suggestion with every new commit to the PR.
🧬 Code graph analysis (2)
src/SharedRest.js (3)
src/RestQuery.js (6)
  • require (7-7)
  • require (9-9)
  • require (10-10)
  • defaultLogger (11-11)
  • detailedError (125-125)
  • detailedError (811-811)
src/rest.js (4)
  • require (15-15)
  • require (329-329)
  • defaultLogger (330-330)
  • detailedError (331-331)
src/SecurityError.js (1)
  • createSanitizedError (12-27)
src/SecurityError.js (4)
src/SharedRest.js (1)
  • defaultLogger (10-10)
src/RestQuery.js (3)
  • log (126-126)
  • log (812-812)
  • defaultLogger (11-11)
src/rest.js (2)
  • log (332-332)
  • defaultLogger (330-330)
src/middlewares.js (8)
  • log (237-237)
  • log (247-247)
  • log (464-464)
  • log (505-505)
  • log (516-516)
  • log (539-539)
  • error (252-252)
  • error (506-506)
🪛 GitHub Check: Lint
src/triggers.js

[failure] 1112-1112:
Newline required at end of file but not found

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (15)
  • GitHub Check: PostgreSQL 18, PostGIS 3.6
  • GitHub Check: PostgreSQL 16, PostGIS 3.5
  • GitHub Check: PostgreSQL 17, PostGIS 3.5
  • GitHub Check: PostgreSQL 15, PostGIS 3.4
  • GitHub Check: PostgreSQL 15, PostGIS 3.3
  • GitHub Check: PostgreSQL 15, PostGIS 3.5
  • GitHub Check: Redis Cache
  • GitHub Check: Node 22
  • GitHub Check: Node 20
  • GitHub Check: Node 18
  • GitHub Check: MongoDB 8, ReplicaSet
  • GitHub Check: MongoDB 7, ReplicaSet
  • GitHub Check: MongoDB 6, ReplicaSet
  • GitHub Check: Docker Build
  • GitHub Check: Benchmarks
🔇 Additional comments (1)
src/triggers.js (1)

1-1112: Verify error sanitization scope in this file.

The PR objective is to sanitize error messages for security. This file contains several error handling paths (e.g., resolveError at lines 669–693, validation error throws at lines 738–741, etc.) that could expose sensitive details. Per the AI summary, this file has "no observable functional changes" aside from the EOF newline.

Please confirm whether error sanitization changes were intentionally omitted from src/triggers.js, or if related changes are in other files within this PR.

@coratgerl coratgerl requested a review from mtrezza November 18, 2025 21:41
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

♻️ Duplicate comments (1)
src/rest.js (1)

171-173: Sanitize the early _User delete guard to align with the rest of the file.

Line 172 still throws a raw Parse.Error(Parse.Error.SESSION_MISSING, 'Insufficient auth to delete user'), exposing operation detail. This contradicts the sanitization applied to handleSessionMissingError at lines 329-332.

Apply this diff to sanitize the error:

   if (className === '_User' && auth.isUnauthenticated()) {
-    throw new Parse.Error(Parse.Error.SESSION_MISSING, 'Insufficient auth to delete user');
+    const { createSanitizedError } = require('./SecurityError');
+    const detailedError = 'Insufficient auth to delete user';
+    throw createSanitizedError(Parse.Error.SESSION_MISSING, detailedError);
   }

Based on learnings

🧹 Nitpick comments (8)
src/GraphQL/parseGraphQLUtils.js (2)

5-12: Master-key enforcement now goes through createSanitizedError; verify client message is actually sanitized.

Routing this OPERATION_FORBIDDEN through createSanitizedError is the right direction for centralizing and sanitizing permission errors, while preserving the error code. One thing to double‑check against #8958: if createSanitizedError forwards its message argument to the client unchanged, this would still expose "unauthorized: master key is required". In that case, either:

  • have createSanitizedError sanitize to a generic message (e.g. "unauthorized"), or
  • pass a pre‑sanitized message here and log the detailed context inside createSanitizedError.

It would be good to have a GraphQL test asserting that the client no longer sees "master key is required" in this path.


1-57: PR meta: Angular-style title suggestion and docs note.

For the changelog, an Angular-style title that reflects this behavior change could be:

  • feat(security): sanitize permission errors for GraphQL

Also, since this subtly changes what clients see on authorization failures, consider a brief note in the README or security/operations docs (e.g., “detailed permission error messages are now logged server-side and hidden from clients”), even if no new option is added.

src/GraphQL/loaders/schemaMutations.js (1)

34-41: Read-only masterKey error is now sanitized in CreateClass.

The implementation correctly uses createSanitizedError to sanitize the error message. However, unlike similar changes in other files (e.g., src/RestWrite.js lines 204-210), this doesn't pass a config-aware logger. The error will still be sanitized, but detailed logging may use a default logger instead of the config's logger.

If you want consistent logging with the config's logger, consider:

         const { config, auth } = context;
 
         enforceMasterKeyAccess(auth, config);
 
         if (auth.isReadOnly) {
+          const log = (config && config.loggerController) || defaultLogger;
           throw createSanitizedError(
             Parse.Error.OPERATION_FORBIDDEN,
             "read-only masterKey isn't allowed to create a schema.",
+            log
           );
         }

(Same pattern applies to UpdateClass at lines 86-89 and DeleteClass at lines 137-140.)

src/Controllers/SchemaController.js (1)

1408-1413: Consider simplifying the error construction pattern.

Per past review feedback, these could be more compact without the intermediate detailedError variable:

       if (!aclGroup || aclGroup.length == 0) {
-        const detailedError = 'Permission denied, user needs to be authenticated.';
-        throw createSanitizedError(Parse.Error.OBJECT_NOT_FOUND, detailedError);
+        throw createSanitizedError(
+          Parse.Error.OBJECT_NOT_FOUND,
+          'Permission denied, user needs to be authenticated.'
+        );
       } else if (aclGroup.indexOf('*') > -1 && aclGroup.length == 1) {
-        const detailedError = 'Permission denied, user needs to be authenticated.';
-        throw createSanitizedError(Parse.Error.OBJECT_NOT_FOUND, detailedError);
+        throw createSanitizedError(
+          Parse.Error.OBJECT_NOT_FOUND,
+          'Permission denied, user needs to be authenticated.'
+        );
       }

This same pattern applies to lines 1426-1427 and 1447-1448 as well.

Based on learnings

src/RestWrite.js (2)

667-669: Good: compact error construction pattern.

The single-line createSanitizedError call is concise and readable, though it doesn't pass a config-aware logger like the implementation at lines 204-210.

For consistency with line 205, consider passing a config-aware logger:

-  if (!this.auth.isMaintenance && !this.auth.isMaster && 'emailVerified' in this.data) {
-    throw createSanitizedError(Parse.Error.OPERATION_FORBIDDEN, 'Clients aren\'t allowed to manually update email verification.');
+  if (!this.auth.isMaintenance && !this.auth.isMaster && 'emailVerified' in this.data) {
+    const log = (this.config && this.config.loggerController) || defaultLogger;
+    throw createSanitizedError(
+      Parse.Error.OPERATION_FORBIDDEN,
+      'Clients aren\'t allowed to manually update email verification.',
+      log
+    );
   }

1456-1462: User modification error is sanitized, but logger parameter could be added.

The implementation correctly uses createSanitizedError, but unlike the pattern at lines 204-210, it doesn't pass a config-aware logger.

For consistency, consider:

     if (this.className === '_User' && this.query && this.auth.isUnauthenticated()) {
       const detailedError = `Cannot modify user ${this.query.objectId}.`;
+      const log = (this.config && this.config.loggerController) || defaultLogger;
       throw createSanitizedError(
         Parse.Error.SESSION_MISSING,
         detailedError,
+        log
       );
     }
src/Routers/PushRouter.js (2)

13-16: Sanitized error use looks correct; optionally align logging and tests

Using createSanitizedError with Parse.Error.OPERATION_FORBIDDEN here keeps the error code stable while delegating message sanitization, which matches the objective of hiding detailed authorization reasons from clients.

Two optional follow-ups:

  • If createSanitizedError supports receiving a logger or request context, consider passing in req.config.loggerController (or a derived logger) so read-only masterKey misuse on /push is logged consistently with other sanitized errors in this PR.
  • Ensure there is a test that exercises this read-only masterKey path and asserts the generic client-facing message while still preserving the correct error code.

1-85: PR title & documentation suggestion

To make the changelog clearer and follow Angular-style conventions, consider renaming the PR to something like:

feat(security): sanitize unauthorized error messages

Also consider a brief note in the changelog (or README, if you think it affects integrators) that certain security-related errors now return generic client messages while logging detailed diagnostics server-side, so users understand why previously detailed messages have become more generic.

Based on learnings

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between a413efd and bb3eef0.

📒 Files selected for processing (17)
  • src/Controllers/SchemaController.js (4 hunks)
  • src/GraphQL/loaders/schemaMutations.js (4 hunks)
  • src/GraphQL/parseGraphQLUtils.js (1 hunks)
  • src/RestQuery.js (5 hunks)
  • src/RestWrite.js (4 hunks)
  • src/Routers/ClassesRouter.js (2 hunks)
  • src/Routers/FilesRouter.js (2 hunks)
  • src/Routers/GlobalConfigRouter.js (2 hunks)
  • src/Routers/GraphQLRouter.js (2 hunks)
  • src/Routers/PurgeRouter.js (1 hunks)
  • src/Routers/PushRouter.js (2 hunks)
  • src/Routers/SchemasRouter.js (4 hunks)
  • src/Routers/UsersRouter.js (2 hunks)
  • src/SecurityError.js (1 hunks)
  • src/SharedRest.js (2 hunks)
  • src/middlewares.js (2 hunks)
  • src/rest.js (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (9)
  • src/Routers/SchemasRouter.js
  • src/Routers/GraphQLRouter.js
  • src/Routers/ClassesRouter.js
  • src/Routers/UsersRouter.js
  • src/Routers/FilesRouter.js
  • src/RestQuery.js
  • src/Routers/PurgeRouter.js
  • src/SecurityError.js
  • src/Routers/GlobalConfigRouter.js
🧰 Additional context used
🧠 Learnings (2)
📓 Common learnings
Learnt from: mtrezza
Repo: parse-community/parse-server PR: 0
File: :0-0
Timestamp: 2025-11-17T15:02:48.786Z
Learning: For Parse Server PRs, always suggest an Angular commit convention PR title that would make a meaningful changelog entry for developers. Update the PR title suggestion on every commit. The format should be: type(scope): description. Common types include feat, fix, perf, refactor, docs, test, chore. The scope should identify the subsystem (e.g., graphql, rest, push, security). The description should be action-oriented and clearly convey the change's impact to developers.
Learnt from: mtrezza
Repo: parse-community/parse-server PR: 0
File: :0-0
Timestamp: 2025-11-08T13:46:04.940Z
Learning: When reviewing Parse Server PRs that add new features, always check whether the feature is documented in the README.md file, though for new Parse Server options this is optional rather than required.
Learnt from: mtrezza
Repo: parse-community/parse-server PR: 0
File: :0-0
Timestamp: 2025-11-17T15:02:24.824Z
Learning: For Parse Server PRs, always suggest an Angular-style PR title that would make a meaningful changelog entry for developers. Update the PR title suggestion with every new commit to the PR.
📚 Learning: 2025-09-21T15:43:32.265Z
Learnt from: mtrezza
Repo: parse-community/parse-server PR: 9858
File: src/GraphQL/ParseGraphQLServer.js:176-178
Timestamp: 2025-09-21T15:43:32.265Z
Learning: The GraphQL playground feature in ParseGraphQLServer.js (applyPlayground method) is intended for development environments only, which is why it includes the master key in client-side headers.

Applied to files:

  • src/GraphQL/loaders/schemaMutations.js
  • src/GraphQL/parseGraphQLUtils.js
🔇 Additional comments (6)
src/GraphQL/parseGraphQLUtils.js (1)

3-3: Import of createSanitizedError looks correct and aligns with new security helper.

The import cleanly plugs this module into the centralized security error handling without changing the public API.

src/rest.js (1)

329-332: LGTM! Session missing error is now sanitized.

The implementation correctly uses createSanitizedError to sanitize the error message while preserving the error code and detailed message for server-side logging.

src/middlewares.js (1)

503-518: LGTM! Master key enforcement is now sanitized.

Both enforceMasterKeyAccess and promiseEnforceMasterKeyAccess correctly use createSanitizedHttpError to sanitize the error response while preserving the detailed message for server-side logging.

src/SharedRest.js (1)

12-35: LGTM! Role security errors are now sanitized.

All three error paths in enforceRoleSecurity correctly use createSanitizedError to sanitize error messages while preserving detailed error information for server-side logging. The implementation is consistent and maintains the original control flow.

src/RestWrite.js (1)

204-210: LGTM! Client class creation error is sanitized with config-aware logging.

The implementation correctly uses createSanitizedError with a config-aware logger, ensuring detailed errors are logged to the configured logger while sending sanitized messages to clients.

src/Routers/PushRouter.js (1)

4-4: createSanitizedError import integrates PushRouter into the sanitization layer

Importing createSanitizedError here is appropriate and keeps push routing aligned with the centralized error-sanitization helper; no issues from a dependency or routing perspective.

coderabbitai[bot]
coderabbitai bot previously approved these changes Nov 18, 2025
@mtrezza
Copy link
Member

mtrezza commented Nov 19, 2025

@coratgerl you can treat coderabbit review comments like human review comments; if you followed it, coderabbit will close its comment after checking your next commit. you should only resolve it manually after providing your arguments. if in doubt you can engage in a discussion. That makes it easier for everyone to know what your position on the feedback is.

coderabbitai[bot]
coderabbitai bot previously approved these changes Nov 19, 2025
coderabbitai[bot]
coderabbitai bot previously approved these changes Nov 19, 2025
@coratgerl coratgerl requested a review from mtrezza November 19, 2025 17:12
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
spec/schemas.spec.js (1)

2513-2605: Fix mismatched action name in CLP 5 sanitized log expectation

In validate CLP 5, the first forbidden query is a find() call, but the sanitized log expectation uses action create:

sanitizedErrorCall.checkMessage(
  'Permission denied for action create on class AClass',
  callCountBefore
);

Given this branch is exercising a find operation, the detailed message should stay aligned with the other CLP tests and the underlying enforcement.

-          sanitizedErrorCall.checkMessage('Permission denied for action create on class AClass', callCountBefore);
+          sanitizedErrorCall.checkMessage('Permission denied for action find on class AClass', callCountBefore);

Without this, the test is likely to fail or become misleading if the implementation correctly reports find as the action.

♻️ Duplicate comments (2)
src/RestWrite.js (1)

1458-1462: Verify logger usage consistency (same concern as line 666).

Similar to the comment at line 666, this createSanitizedError call doesn't pass a logger parameter, while the call at line 205 does. Ensure this is intentional and that the detailed error message (including this.query.objectId) is properly logged server-side.

src/rest.js (1)

171-176: Unauthenticated _User delete still throws raw error, bypassing sanitization helper

The early guard:

if (className === '_User' && auth.isUnauthenticated()) {
  throw new Parse.Error(Parse.Error.SESSION_MISSING, 'Insufficient auth to delete user');
}

still sends a detailed message directly to the client instead of going through createSanitizedError, which is inconsistent with the new sanitized session/auth handling and the behavior in handleSessionMissingError.

To fully align delete‑user with the sanitization strategy, route this branch through createSanitizedError as well:

   if (className === '_User' && auth.isUnauthenticated()) {
-    throw new Parse.Error(Parse.Error.SESSION_MISSING, 'Insufficient auth to delete user');
+    const { createSanitizedError } = require('./Error');
+    throw createSanitizedError(
+      Parse.Error.SESSION_MISSING,
+      'Insufficient auth to delete user'
+    );
   }

(Alternatively, you could delegate to handleSessionMissingError if you prefer a single central helper.)

This avoids leaking operation details to clients while keeping richer information in the server logs.

🧹 Nitpick comments (10)
src/TestUtils.js (1)

85-104: Make logger spy resilient to repeated usage and tightly-coupled prefix

The helper works for current tests, but it assumes spyOn(logger, 'error') is only ever called once per spec and that tests always run with process.env.TESTING set so the first log arg is exactly 'Sanitized error:'. If a spec calls getSanitizedErrorCall() twice on the same logger, Jasmine may throw for double-spying unless allowRespy(true) is configured, and any future change to the prefix would silently break all these tests.

To make this more robust, consider:

  • Reusing an existing spy if one already exists instead of always calling spyOn.
  • Centralizing the 'Sanitized error:' prefix (e.g. exported constant from src/Error.js) so tests and implementation cannot drift.
spec/AudienceRouter.spec.js (1)

5-5: AudienceRouter master-key tests comprehensively validate sanitized errors

All push_audiences authorization tests now assert a generic 'Permission denied' on the client and use getSanitizedErrorCall to confirm the detailed master-key message was logged, covering POST/GET/PUT/DELETE consistently. This is a solid, end‑to‑end check of the new sanitization behavior.

Also applies to: 266-339

spec/ParseFile.spec.js (1)

766-776: Tidy TestUtils import and confirm unauthorized file-path behavior

Two small points here:

  1. Inside "does not crash on file request with invalid app ID", you re-require('../lib/TestUtils') and destructure getSanitizedErrorCall, even though it is already imported at the top of the file. You can use the existing import and drop the inline require to avoid redundancy.

  2. For invalid file paths, the test still expects { error: 'unauthorized' } instead of the generic 'Permission denied' used for invalid application IDs. If the goal is that all unauthorized file routes share the same sanitized client message, it may be worth aligning this case as well; otherwise, clarifying in a comment or issue which cases are deliberately left as 'unauthorized' could prevent confusion later.

Also applies to: 782-788

src/Error.js (1)

1-44: Centralized sanitization helpers look good; consider minor hardening and docs/changelog

The new helpers encapsulate the pattern of:

  • Logging the detailed message via defaultLogger.error(...), with a 'Sanitized error:' prefix under TESTING so specs can detect it.
  • Returning a Parse.Error or plain Error that always exposes the generic 'Permission denied' string while preserving the original error or HTTP status code.

A few small follow‑ups to consider:

  • Avoid duplicate logging logic: Extract a small internal helper (e.g. logSanitizedError(detailedMessage)) shared by both functions to keep the logging behavior in one place and reduce drift risk if the format ever changes.
  • Be explicit about Parse dependency: If feasible within this codebase’s conventions, importing Parse (rather than relying on a global) would make createSanitizedError more self‑contained and tooling‑friendly.
  • Lock in the test contract: The tests rely on the 'Sanitized error:' prefix only being used in TESTING. If any other code path ever starts logging with that same prefix, the TestUtils helper may get false positives; conversely, changing the prefix will break many specs. It may be worth documenting this contract in a short comment near the export or in CONTRIBUTING.

Given this is a security‑relevant behavior change visible to clients, please also:

  • Add or update a short note in the appropriate docs/README or migration/changelog area to mention that certain authorization failures now return a generic "Permission denied" message while keeping error codes unchanged.

  • Update the PR title to an Angular-style form suitable for the changelog, for example:

    feat(security): sanitize unauthorized error messages

src/RestWrite.js (1)

204-209: Consider using optional chaining for consistency.

The logger retrieval works correctly, but for consistency with the patterns in src/middlewares.js (lines 236, 246, 463, 536), consider using optional chaining:

Apply this diff:

-          const log = (this.config && this.config.loggerController) || defaultLogger;
+          const log = this.config?.loggerController || defaultLogger;
spec/schemas.spec.js (2)

7-8: Avoid double requiring ../lib/TestUtils

You now import ../lib/TestUtils twice (as TestUtils and via destructuring). To keep things consistent with other specs (e.g. spec/ParseAPI.spec.js) and avoid redundancy, consider a single destructured import:

-const TestUtils = require('../lib/TestUtils');
-const { getSanitizedErrorCall } = require('../lib/TestUtils');
+const { destroyAllDataPermanently, getSanitizedErrorCall } = require('../lib/TestUtils');

and then call destroyAllDataPermanently directly where needed.


170-190: Strengthen schema auth tests by asserting error code as well

These two tests now correctly assert the sanitized message 'Permission denied', but they still rely only on status + message. To better pin down the behavior of the sanitized error, also assert the error code (e.g. Parse.Error.OPERATION_FORBIDDEN) in both tests.

   }).then(fail, response => {
     expect(response.status).toEqual(403);
-    expect(response.data.error).toEqual('Permission denied');
+    expect(response.data.code).toEqual(Parse.Error.OPERATION_FORBIDDEN);
+    expect(response.data.error).toEqual('Permission denied');

Apply the same pattern to the following test that covers /schemas with REST key.

spec/Schema.spec.js (1)

1449-1727: Consider asserting error codes in required-auth tests as well

The required-auth tests (find, create, get, find not authenticated) now:

  • Assert the generic 'Permission denied' message.
  • Use getSanitizedErrorCall to verify the detailed log message 'Permission denied, user needs to be authenticated.'.

To further harden these tests against regressions, you may want to also assert the error code (e.g., Parse.Error.OPERATION_FORBIDDEN) alongside the message in each of these cases, similar to the CLP tests above.

-        e => {
-          expect(e.message).toEqual('Permission denied');
+        e => {
+          expect(e.code).toEqual(Parse.Error.OPERATION_FORBIDDEN);
+          expect(e.message).toEqual('Permission denied');
           sanitizedErrorCall.checkMessage(
             'Permission denied, user needs to be authenticated.',
             callCountBefore
           );

This is optional but would make the required-auth suite consistent with the other permission tests.

spec/ParseAPI.spec.js (1)

1713-1733: Purge-without-master-key test correctly exercises sanitized error; consider tightening assertions

The updated test now:

  • Uses getSanitizedErrorCall and callCountBefore.
  • Asserts the client-facing response.data.error is 'Permission denied'.
  • Verifies the logged detailed message contains 'unauthorized: master key is required'.

To fully specify the behavior, you could also assert the HTTP status and/or error code, e.g.:

   .catch(response => {
-    expect(response.data.error).toEqual('Permission denied');
+    expect(response.status).toBe(403);
+    expect(response.data.code).toBe(Parse.Error.OPERATION_FORBIDDEN);
+    expect(response.data.error).toEqual('Permission denied');
     sanitizedErrorCall.checkMessage(
       'unauthorized: master key is required',
       callCountBefore
     );

Not required, but this would make the test more robust.

src/Routers/GraphQLRouter.js (1)

4-24: Read‑only master key now returns a sanitized forbidden error for GraphQL config updates

Using:

throw createSanitizedError(
  Parse.Error.OPERATION_FORBIDDEN,
  "read-only masterKey isn't allowed to update the GraphQL config.",
);

on req.auth.isReadOnly ensures:

  • Clients only see a generic “Permission denied” (via the helper).
  • Logs still carry the detailed reason, matching the rest of the sanitization work.

For the changelog, an Angular-style PR title that reflects this broader change could be:

feat(security): sanitize permission errors

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 79ed425 and 767aba0.

📒 Files selected for processing (31)
  • spec/AudienceRouter.spec.js (2 hunks)
  • spec/LogsRouter.spec.js (3 hunks)
  • spec/ParseAPI.spec.js (5 hunks)
  • spec/ParseFile.spec.js (6 hunks)
  • spec/ParseGlobalConfig.spec.js (3 hunks)
  • spec/ParseGraphQLServer.spec.js (12 hunks)
  • spec/ParseQuery.Aggregate.spec.js (2 hunks)
  • spec/ParseUser.spec.js (8 hunks)
  • spec/RestQuery.spec.js (3 hunks)
  • spec/Schema.spec.js (15 hunks)
  • spec/features.spec.js (3 hunks)
  • spec/rest.spec.js (12 hunks)
  • spec/schemas.spec.js (19 hunks)
  • src/Controllers/SchemaController.js (4 hunks)
  • src/Error.js (1 hunks)
  • src/GraphQL/loaders/schemaMutations.js (4 hunks)
  • src/GraphQL/parseGraphQLUtils.js (1 hunks)
  • src/RestQuery.js (5 hunks)
  • src/RestWrite.js (4 hunks)
  • src/Routers/ClassesRouter.js (2 hunks)
  • src/Routers/FilesRouter.js (2 hunks)
  • src/Routers/GlobalConfigRouter.js (2 hunks)
  • src/Routers/GraphQLRouter.js (2 hunks)
  • src/Routers/PurgeRouter.js (1 hunks)
  • src/Routers/PushRouter.js (2 hunks)
  • src/Routers/SchemasRouter.js (4 hunks)
  • src/Routers/UsersRouter.js (2 hunks)
  • src/SharedRest.js (2 hunks)
  • src/TestUtils.js (1 hunks)
  • src/middlewares.js (2 hunks)
  • src/rest.js (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (16)
  • src/Routers/FilesRouter.js
  • src/Routers/PurgeRouter.js
  • src/middlewares.js
  • src/Routers/SchemasRouter.js
  • spec/ParseQuery.Aggregate.spec.js
  • spec/RestQuery.spec.js
  • spec/rest.spec.js
  • src/Controllers/SchemaController.js
  • src/SharedRest.js
  • spec/features.spec.js
  • src/RestQuery.js
  • src/GraphQL/loaders/schemaMutations.js
  • spec/ParseGraphQLServer.spec.js
  • src/Routers/ClassesRouter.js
  • src/GraphQL/parseGraphQLUtils.js
  • src/Routers/GlobalConfigRouter.js
🧰 Additional context used
🧠 Learnings (9)
📓 Common learnings
Learnt from: mtrezza
Repo: parse-community/parse-server PR: 0
File: :0-0
Timestamp: 2025-11-08T13:46:04.940Z
Learning: When reviewing Parse Server PRs that add new features, always check whether the feature is documented in the README.md file, though for new Parse Server options this is optional rather than required.
Learnt from: mtrezza
Repo: parse-community/parse-server PR: 0
File: :0-0
Timestamp: 2025-11-17T15:02:48.786Z
Learning: For Parse Server PRs, always suggest an Angular commit convention PR title that would make a meaningful changelog entry for developers. Update the PR title suggestion on every commit. The format should be: type(scope): description. Common types include feat, fix, perf, refactor, docs, test, chore. The scope should identify the subsystem (e.g., graphql, rest, push, security). The description should be action-oriented and clearly convey the change's impact to developers.
Learnt from: mtrezza
Repo: parse-community/parse-server PR: 0
File: :0-0
Timestamp: 2025-11-17T15:02:24.824Z
Learning: For Parse Server PRs, always suggest an Angular-style PR title that would make a meaningful changelog entry for developers. Update the PR title suggestion with every new commit to the PR.
📚 Learning: 2025-09-21T15:43:32.265Z
Learnt from: mtrezza
Repo: parse-community/parse-server PR: 9858
File: src/GraphQL/ParseGraphQLServer.js:176-178
Timestamp: 2025-09-21T15:43:32.265Z
Learning: The GraphQL playground feature in ParseGraphQLServer.js (applyPlayground method) is intended for development environments only, which is why it includes the master key in client-side headers.

Applied to files:

  • src/Routers/GraphQLRouter.js
📚 Learning: 2025-10-16T19:27:05.311Z
Learnt from: Moumouls
Repo: parse-community/parse-server PR: 9883
File: spec/CloudCodeLogger.spec.js:410-412
Timestamp: 2025-10-16T19:27:05.311Z
Learning: In spec/CloudCodeLogger.spec.js, the test "should log cloud function triggers using the silent log level" (around lines 383-420) is known to be flaky and requires the extra `await new Promise(resolve => setTimeout(resolve, 100))` timeout after awaiting `afterSavePromise` for reliability, even though it may appear redundant.

Applied to files:

  • spec/AudienceRouter.spec.js
  • spec/schemas.spec.js
  • spec/ParseUser.spec.js
  • spec/LogsRouter.spec.js
  • spec/Schema.spec.js
📚 Learning: 2025-05-09T09:59:06.289Z
Learnt from: mtrezza
Repo: parse-community/parse-server PR: 9445
File: spec/ParseLiveQuery.spec.js:1340-1375
Timestamp: 2025-05-09T09:59:06.289Z
Learning: Tests in the parse-server repository should use promise-based approaches rather than callback patterns with `done()`. Use a pattern where a Promise is created that resolves when the event occurs, then await that promise.

Applied to files:

  • spec/AudienceRouter.spec.js
  • spec/schemas.spec.js
  • spec/ParseUser.spec.js
  • spec/Schema.spec.js
  • spec/ParseAPI.spec.js
📚 Learning: 2025-05-04T20:41:05.147Z
Learnt from: mtrezza
Repo: parse-community/parse-server PR: 9445
File: spec/ParseLiveQuery.spec.js:1312-1338
Timestamp: 2025-05-04T20:41:05.147Z
Learning: New tests in the parse-server repository should use async/await with promise-based patterns rather than callback patterns with `done()`.

Applied to files:

  • spec/AudienceRouter.spec.js
  • spec/schemas.spec.js
  • spec/ParseUser.spec.js
  • spec/Schema.spec.js
  • spec/ParseAPI.spec.js
📚 Learning: 2025-05-09T09:59:06.289Z
Learnt from: mtrezza
Repo: parse-community/parse-server PR: 9445
File: spec/ParseLiveQuery.spec.js:1340-1375
Timestamp: 2025-05-09T09:59:06.289Z
Learning: New tests in the parse-server repository should use async/await with promise-based patterns rather than callback patterns with `done()`. The preferred pattern is to create a Promise that resolves when an expected event occurs, then await that Promise.

Applied to files:

  • spec/AudienceRouter.spec.js
  • spec/schemas.spec.js
  • spec/ParseUser.spec.js
  • spec/Schema.spec.js
  • spec/ParseAPI.spec.js
📚 Learning: 2025-04-30T19:31:35.344Z
Learnt from: RahulLanjewar93
Repo: parse-community/parse-server PR: 9744
File: spec/ParseLiveQuery.spec.js:0-0
Timestamp: 2025-04-30T19:31:35.344Z
Learning: In the Parse Server codebase, the functions in QueryTools.js are typically tested through end-to-end behavior tests rather than direct unit tests, even though the functions are exported from the module.

Applied to files:

  • spec/AudienceRouter.spec.js
  • spec/schemas.spec.js
  • spec/Schema.spec.js
  • spec/ParseAPI.spec.js
📚 Learning: 2025-08-27T12:33:06.237Z
Learnt from: EmpiDev
Repo: parse-community/parse-server PR: 9770
File: src/triggers.js:467-477
Timestamp: 2025-08-27T12:33:06.237Z
Learning: In the Parse Server codebase, maybeRunAfterFindTrigger is called in production with Parse.Query objects constructed via withJSON(), so the plain object query handling bug only affects tests, not production code paths.

Applied to files:

  • spec/schemas.spec.js
  • spec/Schema.spec.js
📚 Learning: 2025-08-27T09:08:34.252Z
Learnt from: EmpiDev
Repo: parse-community/parse-server PR: 9770
File: src/triggers.js:446-454
Timestamp: 2025-08-27T09:08:34.252Z
Learning: When analyzing function signature changes in Parse Server codebase, verify that call sites are actually incorrect before flagging them. Passing tests are a strong indicator that function calls are already properly aligned with new signatures.

Applied to files:

  • spec/ParseUser.spec.js
  • spec/ParseAPI.spec.js
🧬 Code graph analysis (12)
src/rest.js (1)
src/RestQuery.js (3)
  • require (7-7)
  • require (9-9)
  • require (10-10)
src/TestUtils.js (3)
spec/ParseLiveQuery.spec.js (4)
  • logger (299-299)
  • logger (589-589)
  • logger (632-632)
  • require (8-8)
spec/ParseAPI.spec.js (1)
  • require (9-9)
spec/ParseConfigKey.spec.js (1)
  • loggerErrorSpy (5-5)
src/Error.js (2)
src/RestQuery.js (1)
  • defaultLogger (11-11)
src/middlewares.js (2)
  • error (252-252)
  • error (505-505)
spec/AudienceRouter.spec.js (2)
src/TestUtils.js (1)
  • getSanitizedErrorCall (85-104)
src/middlewares.js (2)
  • error (252-252)
  • error (505-505)
spec/schemas.spec.js (1)
src/TestUtils.js (1)
  • getSanitizedErrorCall (85-104)
spec/ParseGlobalConfig.spec.js (1)
src/TestUtils.js (1)
  • getSanitizedErrorCall (85-104)
spec/ParseUser.spec.js (1)
src/TestUtils.js (1)
  • getSanitizedErrorCall (85-104)
spec/LogsRouter.spec.js (1)
src/TestUtils.js (1)
  • getSanitizedErrorCall (85-104)
spec/Schema.spec.js (2)
spec/ParseAPI.spec.js (2)
  • require (9-9)
  • Parse (6-6)
src/TestUtils.js (1)
  • getSanitizedErrorCall (85-104)
src/RestWrite.js (2)
src/middlewares.js (4)
  • log (237-237)
  • log (247-247)
  • log (464-464)
  • log (537-537)
src/RestQuery.js (1)
  • defaultLogger (11-11)
spec/ParseAPI.spec.js (1)
src/TestUtils.js (2)
  • destroyAllDataPermanently (8-28)
  • getSanitizedErrorCall (85-104)
spec/ParseFile.spec.js (2)
spec/ParseAPI.spec.js (1)
  • require (9-9)
src/TestUtils.js (1)
  • getSanitizedErrorCall (85-104)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (15)
  • GitHub Check: Docker Build
  • GitHub Check: Benchmarks
  • GitHub Check: Node 22
  • GitHub Check: PostgreSQL 18, PostGIS 3.6
  • GitHub Check: PostgreSQL 17, PostGIS 3.5
  • GitHub Check: PostgreSQL 15, PostGIS 3.3
  • GitHub Check: Node 18
  • GitHub Check: Node 20
  • GitHub Check: PostgreSQL 16, PostGIS 3.5
  • GitHub Check: PostgreSQL 15, PostGIS 3.5
  • GitHub Check: PostgreSQL 15, PostGIS 3.4
  • GitHub Check: Redis Cache
  • GitHub Check: MongoDB 8, ReplicaSet
  • GitHub Check: MongoDB 6, ReplicaSet
  • GitHub Check: MongoDB 7, ReplicaSet
🔇 Additional comments (18)
spec/LogsRouter.spec.js (1)

4-5: LogsRouter master-key test correctly validates sanitized vs logged detail

The updated test now asserts the client sees a generic 403 / 'Permission denied' while also verifying that the logger captured 'unauthorized: master key is required' via getSanitizedErrorCall, which is exactly what this PR aims for.

Also applies to: 55-70

spec/ParseGlobalConfig.spec.js (1)

5-5: GlobalConfig master-key test aligns with sanitized error behavior

The test now correctly expects a generic 'Permission denied' on the client while asserting that the logger recorded 'unauthorized: master key is required' using getSanitizedErrorCall, matching the new sanitization contract.

Also applies to: 223-242

spec/ParseUser.spec.js (4)

15-15: New TestUtils import is appropriate for user-related security tests

Importing getSanitizedErrorCall here is appropriate, as multiple user/session flows now verify that detailed messages are logged while clients receive only sanitized text.


2635-2670: Confirm intent to sanitize this specific invalid-session-token REST path

In it('cannot delete session if no sessionToken'), the first PUT /sessions/:id with an invalid session token now returns 'Permission denied' (code 209), while the test asserts the detailed 'Invalid session token' message is only present in the logs via getSanitizedErrorCall.

Elsewhere in this file, some flows (e.g. "Invalid session tokens are rejected") still expect the full 'Invalid session token' text in the HTTP response. Please confirm that this asymmetry is intentional (REST PUT /sessions/:id sanitized, other session-token checks not) and not a missed conversion.


3363-3394: Email-verification test correctly distinguishes client vs logged messages

The "should not allow updates to emailVerified" test now:

  • Installs a sanitized logger spy after reconfiguration.
  • Expects the client error message 'Permission denied'.
  • Verifies the logged detail "Clients aren't allowed to manually update email verification.".

This precisely exercises the new sanitization helper and protects against regressions in the sensitive email-verification path.


4387-4413: loginAs master-key test is well-aligned with sanitized HTTP errors

For loginAs without master key, the test now:

  • Tracks sanitized logger calls with getSanitizedErrorCall.
  • Expects OPERATION_FORBIDDEN with client message 'Permission denied'.
  • Asserts the logged detail contains 'master key is required'.

Using a substring for the logged message (toContain) gives some flexibility if the detailed text is extended, while still verifying that the critical part is logged.

spec/ParseFile.spec.js (1)

8-8: File deletion tests now robustly cover sanitized master-key errors

The "blocks file deletions with missing or incorrect master-key header" test:

  • Correctly expects 403 / 'Permission denied' for both missing and wrong master key.
  • Uses getSanitizedErrorCall with separate callCountBefore values to assert that detailed 'unauthorized: master key is required' messages were logged for each attempt.

This gives strong coverage of both branches of the authorization check.

Also applies to: 135-184

src/RestWrite.js (2)

20-21: LGTM!

The import statements are correct and follow the same pattern used in other files like src/middlewares.js and src/RestQuery.js.


666-669: ****

The function signature for createSanitizedError (src/Error.js:11) accepts only 2 parameters: errorCode and detailedMessage. The call at line 205 does not pass a logger parameter—it only passes 2 parameters, identical to line 666. All 26+ usages throughout the codebase consistently use 2 parameters only. The code is correct and consistent; no changes are needed.

Likely an incorrect or invalid review comment.

spec/schemas.spec.js (2)

1811-1844: Good coverage of sanitized CLP error for addField

This test now:

  • Asserts the client-facing message 'Permission denied'.
  • Verifies err.code === Parse.Error.OPERATION_FORBIDDEN.
  • Uses getSanitizedErrorCall to assert the detailed log message ('Permission denied for action addField on class AClass').

That combination exercises both the sanitized surface and the logged detail as intended.


2175-2511: CLP find tests now validate both error code and sanitized log

The validate CLP 1–4 tests correctly:

  • Capture the logger error call count before the forbidden query.
  • Assert the thrown error has message 'Permission denied' and code Parse.Error.OPERATION_FORBIDDEN.
  • Assert the logged detailed message uses the correct action (find) and class name.

This matches the new sanitized error behavior and addresses the previous request to also test the error code.

spec/Schema.spec.js (2)

5-5: Importing getSanitizedErrorCall here is appropriate

Pulling getSanitizedErrorCall from ../lib/TestUtils matches how other specs hook into the sanitized error logger and keeps this file aligned with the new test utilities.


252-297: CLP count test now fully validates sanitized error behavior

The updated class-level permissions test count:

  • Captures logger call count before the forbidden query.count().
  • Asserts err.message === 'Permission denied'.
  • Asserts err.code === Parse.Error.OPERATION_FORBIDDEN.
  • Verifies the logged detailed message ('Permission denied for action count on class Stuff').

This is a solid end-to-end check of both the client-visible and logged aspects of the sanitized error.

spec/ParseAPI.spec.js (2)

9-9: Destructured TestUtils import is clearer and consistent

Switching to:

const { getSanitizedErrorCall, destroyAllDataPermanently } = require('../lib/TestUtils');

simplifies call sites and matches the pattern used elsewhere in the suite.


164-208: destroyAllDataPermanently() usage remains correct after refactor

These tests now call destroyAllDataPermanently() directly instead of via TestUtils., while preserving the same promise chain. Behavior and semantics remain unchanged.

src/Routers/PushRouter.js (1)

11-21: Push with read‑only master key now uses sanitized permission error

The new guard:

if (req.auth.isReadOnly) {
  throw createSanitizedError(
    Parse.Error.OPERATION_FORBIDDEN,
    "read-only masterKey isn't allowed to send push notifications.",
  );
}

correctly routes this case through the sanitization helper, so clients see a generic forbidden error while logs retain detail.

src/rest.js (1)

321-332: Session‑missing path for _User now sanitized through createSanitizedError

handleSessionMissingError now transforms an _User OBJECT_NOT_FOUND under non‑master auth into:

const { createSanitizedError } = require('./Error');
throw createSanitizedError(Parse.Error.SESSION_MISSING, 'Insufficient auth.');

This keeps the error code (SESSION_MISSING) while ensuring the client only sees the generic sanitized message, with the detailed context available in logs.

src/Routers/UsersRouter.js (1)

335-373: /loginAs now correctly uses sanitized permission error for non‑master calls

The updated guard:

if (!req.auth.isMaster) {
  throw createSanitizedError(
    Parse.Error.OPERATION_FORBIDDEN,
    'master key is required',
  );
}

ensures /loginAs:

  • Still enforces master‑key only access.
  • Returns a sanitized forbidden error to clients, while logging the specific reason, in line with the rest of the sanitization work.

coderabbitai[bot]
coderabbitai bot previously approved these changes Nov 21, 2025
@parse-community parse-community deleted a comment from coderabbitai bot Nov 22, 2025
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (1)
src/RestWrite.js (1)

1-1861: Consider improving the PR title per Angular commit convention.

Based on learnings, the PR title should follow the Angular commit convention format (type(scope): description) and make a meaningful changelog entry for developers.

Current title: feat: Sanitize error for security

Suggested improvement: fix(security): sanitize authorization error messages to prevent information disclosure

This better conveys that:

  • It's a security fix (not a new feature)
  • It addresses information disclosure
  • It's specifically about authorization errors
  • It provides value as a changelog entry

Based on learnings

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 239a55c and c15ff72.

📒 Files selected for processing (3)
  • src/RestQuery.js (5 hunks)
  • src/RestWrite.js (5 hunks)
  • src/rest.js (3 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/rest.js
🧰 Additional context used
🧠 Learnings (9)
📓 Common learnings
Learnt from: mtrezza
Repo: parse-community/parse-server PR: 0
File: :0-0
Timestamp: 2025-11-08T13:46:04.940Z
Learning: When reviewing Parse Server PRs that add new features, always check whether the feature is documented in the README.md file, though for new Parse Server options this is optional rather than required.
Learnt from: mtrezza
Repo: parse-community/parse-server PR: 0
File: :0-0
Timestamp: 2025-11-17T15:02:48.786Z
Learning: For Parse Server PRs, always suggest an Angular commit convention PR title that would make a meaningful changelog entry for developers. Update the PR title suggestion on every commit. The format should be: type(scope): description. Common types include feat, fix, perf, refactor, docs, test, chore. The scope should identify the subsystem (e.g., graphql, rest, push, security). The description should be action-oriented and clearly convey the change's impact to developers.
📚 Learning: 2025-08-27T09:08:34.252Z
Learnt from: EmpiDev
Repo: parse-community/parse-server PR: 9770
File: src/triggers.js:446-454
Timestamp: 2025-08-27T09:08:34.252Z
Learning: When analyzing function signature changes in Parse Server codebase, verify that call sites are actually incorrect before flagging them. Passing tests are a strong indicator that function calls are already properly aligned with new signatures.

Applied to files:

  • src/RestWrite.js
📚 Learning: 2025-11-08T13:46:04.940Z
Learnt from: mtrezza
Repo: parse-community/parse-server PR: 0
File: :0-0
Timestamp: 2025-11-08T13:46:04.940Z
Learning: When reviewing Parse Server PRs that add new features, always check whether the feature is documented in the README.md file, though for new Parse Server options this is optional rather than required.

Applied to files:

  • src/RestWrite.js
📚 Learning: 2025-11-17T15:02:24.824Z
Learnt from: mtrezza
Repo: parse-community/parse-server PR: 0
File: :0-0
Timestamp: 2025-11-17T15:02:24.824Z
Learning: For Parse Server PRs, always suggest an Angular-style PR title that would make a meaningful changelog entry for developers. Update the PR title suggestion with every new commit to the PR.

Applied to files:

  • src/RestWrite.js
📚 Learning: 2025-11-17T15:02:48.786Z
Learnt from: mtrezza
Repo: parse-community/parse-server PR: 0
File: :0-0
Timestamp: 2025-11-17T15:02:48.786Z
Learning: For Parse Server PRs, always suggest an Angular commit convention PR title that would make a meaningful changelog entry for developers. Update the PR title suggestion on every commit. The format should be: type(scope): description. Common types include feat, fix, perf, refactor, docs, test, chore. The scope should identify the subsystem (e.g., graphql, rest, push, security). The description should be action-oriented and clearly convey the change's impact to developers.

Applied to files:

  • src/RestWrite.js
📚 Learning: 2025-11-08T13:46:04.940Z
Learnt from: mtrezza
Repo: parse-community/parse-server PR: 0
File: :0-0
Timestamp: 2025-11-08T13:46:04.940Z
Learning: For new Parse Server options, verify that the option is documented in src/Options/index.js and that npm run definitions has been executed to reflect changes in src/Options/docs.js and src/Options/Definitions.js. README.md documentation is a bonus but not required for new options.

Applied to files:

  • src/RestWrite.js
📚 Learning: 2025-08-26T14:06:31.853Z
Learnt from: EmpiDev
Repo: parse-community/parse-server PR: 9770
File: spec/CloudCode.spec.js:446-469
Timestamp: 2025-08-26T14:06:31.853Z
Learning: In the Parse Server codebase, when handling query objects in maybeRunAfterFindTrigger, objects without a where property that contain options like limit/skip should be treated as query JSON with an empty where clause using the spread pattern { where: {}, ...query }, not nested as { where: query }.

Applied to files:

  • src/RestWrite.js
📚 Learning: 2025-10-16T19:27:05.311Z
Learnt from: Moumouls
Repo: parse-community/parse-server PR: 9883
File: spec/CloudCodeLogger.spec.js:410-412
Timestamp: 2025-10-16T19:27:05.311Z
Learning: In spec/CloudCodeLogger.spec.js, the test "should log cloud function triggers using the silent log level" (around lines 383-420) is known to be flaky and requires the extra `await new Promise(resolve => setTimeout(resolve, 100))` timeout after awaiting `afterSavePromise` for reliability, even though it may appear redundant.

Applied to files:

  • src/RestWrite.js
📚 Learning: 2025-08-27T12:33:06.237Z
Learnt from: EmpiDev
Repo: parse-community/parse-server PR: 9770
File: src/triggers.js:467-477
Timestamp: 2025-08-27T12:33:06.237Z
Learning: In the Parse Server codebase, maybeRunAfterFindTrigger is called in production with Parse.Query objects constructed via withJSON(), so the plain object query handling bug only affects tests, not production code paths.

Applied to files:

  • src/RestQuery.js
🧬 Code graph analysis (1)
src/RestQuery.js (2)
src/rest.js (2)
  • require (15-15)
  • require (16-16)
src/SharedRest.js (1)
  • require (9-9)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (15)
  • GitHub Check: PostgreSQL 16, PostGIS 3.5
  • GitHub Check: PostgreSQL 18, PostGIS 3.6
  • GitHub Check: PostgreSQL 15, PostGIS 3.5
  • GitHub Check: PostgreSQL 17, PostGIS 3.5
  • GitHub Check: Redis Cache
  • GitHub Check: Node 18
  • GitHub Check: Node 22
  • GitHub Check: MongoDB 8, ReplicaSet
  • GitHub Check: PostgreSQL 15, PostGIS 3.3
  • GitHub Check: PostgreSQL 15, PostGIS 3.4
  • GitHub Check: Node 20
  • GitHub Check: MongoDB 6, ReplicaSet
  • GitHub Check: MongoDB 7, ReplicaSet
  • GitHub Check: Docker Build
  • GitHub Check: Benchmarks
🔇 Additional comments (10)
src/RestQuery.js (5)

10-10: LGTM: Import aligns with sanitized error pattern.

The import of createSanitizedError is consistent with the pattern used in other files (src/rest.js, src/SharedRest.js) and supports the security error sanitization objective.


124-124: LGTM: Session validation error properly sanitized.

Replacing the direct Parse.Error with createSanitizedError ensures that detailed session token information is logged server-side while returning a generic "Permission denied" message to clients, aligning with the security objectives.


425-428: LGTM: Non-existent class error properly sanitized.

Using createSanitizedError here prevents exposing the non-existent class name to clients while preserving it in server logs. This aligns with the PR objective to sanitize authorization-related error messages.


804-807: LGTM: Protected field error properly sanitized.

Converting this error to use createSanitizedError prevents clients from discovering protected field names and class details through error messages. The detailed information remains available in server logs for debugging.


55-55: Update enforceRoleSecurity function signature to accept config parameter.

The function definition in src/SharedRest.js:12 accepts only 3 parameters (method, className, auth), but the call in src/RestQuery.js:55 passes 4 parameters including config. The function signature must be updated to:

function enforceRoleSecurity(method, className, auth, config) {

All other call sites in src/rest.js continue to pass 3 parameters and will work with this change.

⛔ Skipped due to learnings
Learnt from: EmpiDev
Repo: parse-community/parse-server PR: 9770
File: src/triggers.js:446-454
Timestamp: 2025-08-27T09:08:34.252Z
Learning: When analyzing function signature changes in Parse Server codebase, verify that call sites are actually incorrect before flagging them. Passing tests are a strong indicator that function calls are already properly aligned with new signatures.
src/RestWrite.js (5)

20-20: LGTM! Import statement is correct.

The import of createSanitizedError is properly placed and follows the module import conventions.


31-37: LGTM! Read-only master key check correctly sanitized.

The error sanitization appropriately logs the detailed reason server-side while returning a generic permission error to the client, preventing exposure of internal authorization details about the read-only master key.


191-212: LGTM! Client class creation validation correctly sanitized.

The error sanitization appropriately logs the specific class name server-side for debugging while returning a generic permission error to the client, preventing information disclosure about internal class structure and authorization rules.


657-668: LGTM! Email verification field restriction correctly sanitized.

The error sanitization prevents clients from discovering the internal restriction on manual emailVerified updates while logging the detailed reason server-side.


1442-1459: LGTM! Unauthenticated user modification check correctly sanitized.

The error sanitization appropriately logs the specific user objectId server-side while returning a generic session error to the client, preventing enumeration of valid user IDs.

coderabbitai[bot]
coderabbitai bot previously approved these changes Nov 22, 2025
@mtrezza mtrezza changed the title feat: Sanitize error for security fix: Remove potentially sensitive information from error messages returned to clients Nov 22, 2025
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (1)
src/Routers/ClassesRouter.js (1)

7-7: Remove unused import.

The defaultLogger import is not used anywhere in this file. If createSanitizedError handles logging internally (as suggested by the PR objectives), this import can be removed.

Apply this diff to remove the unused import:

-import defaultLogger from '../logger';
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between c15ff72 and 8d78518.

📒 Files selected for processing (3)
  • src/Routers/ClassesRouter.js (2 hunks)
  • src/Routers/FilesRouter.js (2 hunks)
  • src/middlewares.js (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • src/Routers/FilesRouter.js
  • src/middlewares.js
🧰 Additional context used
🧠 Learnings (2)
📓 Common learnings
Learnt from: mtrezza
Repo: parse-community/parse-server PR: 0
File: :0-0
Timestamp: 2025-11-08T13:46:04.940Z
Learning: When reviewing Parse Server PRs that add new features, always check whether the feature is documented in the README.md file, though for new Parse Server options this is optional rather than required.
Learnt from: mtrezza
Repo: parse-community/parse-server PR: 0
File: :0-0
Timestamp: 2025-11-17T15:02:48.786Z
Learning: For Parse Server PRs, always suggest an Angular commit convention PR title that would make a meaningful changelog entry for developers. Update the PR title suggestion on every commit. The format should be: type(scope): description. Common types include feat, fix, perf, refactor, docs, test, chore. The scope should identify the subsystem (e.g., graphql, rest, push, security). The description should be action-oriented and clearly convey the change's impact to developers.
📚 Learning: 2025-04-30T19:31:35.344Z
Learnt from: RahulLanjewar93
Repo: parse-community/parse-server PR: 9744
File: spec/ParseLiveQuery.spec.js:0-0
Timestamp: 2025-04-30T19:31:35.344Z
Learning: In the Parse Server codebase, the functions in QueryTools.js are typically tested through end-to-end behavior tests rather than direct unit tests, even though the functions are exported from the module.

Applied to files:

  • src/Routers/ClassesRouter.js
🔇 Additional comments (2)
src/Routers/ClassesRouter.js (2)

58-58: The selective sanitization application appears intentional, but verify OBJECT_NOT_FOUND inconsistency across the codebase.

Analysis shows createSanitizedError is intentionally applied to security-sensitive errors (authorization, authentication) while validation/query errors remain unsanitized for client feedback. However, OBJECT_NOT_FOUND is inconsistently handled: it's sanitized in SchemaController.js (lines 1408, 1413) but not in ClassesRouter.js line 93. The INVALID_QUERY errors at lines 58 and 183 don't appear to be sanitized anywhere in the codebase, suggesting this is intentional for validation feedback. Consider either: (1) verifying the OBJECT_NOT_FOUND inconsistency is intentional, or (2) applying consistent sanitization if line 93 should be treated as security-sensitive.


116-116: The change is correct and properly implements error sanitization for authorization failures.

The createSanitizedError function correctly logs the detailed message ('Invalid object ID.') server-side via defaultLogger.error() while returning a generic 'Permission denied' message to the client. This aligns with the PR objective to prevent sensitive information leakage.

The selective sanitization at line 116 (OPERATION_FORBIDDEN) while leaving other errors unsanitized (lines 58, 93, 183 - which are INVALID_QUERY and OBJECT_NOT_FOUND in non-permission contexts) is intentional and consistent with the codebase pattern: authorization errors are sanitized to avoid leaking implementation details, while validation errors remain specific to help clients understand their mistakes.

@mtrezza mtrezza changed the title fix: Remove potentially sensitive information from error messages returned to clients fix: Server internal error details leaking in error messages returned to clients Nov 22, 2025
Copy link
Member

@mtrezza mtrezza left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good, just the failing lint

coderabbitai[bot]
coderabbitai bot previously approved these changes Nov 22, 2025
@mtrezza mtrezza merged commit 50edb5a into parse-community:alpha Nov 23, 2025
24 of 28 checks passed
parseplatformorg pushed a commit that referenced this pull request Nov 23, 2025
# [8.5.0-alpha.13](8.5.0-alpha.12...8.5.0-alpha.13) (2025-11-23)

### Bug Fixes

* Server internal error details leaking in error messages returned to clients ([#9937](#9937)) ([50edb5a](50edb5a))
@parseplatformorg
Copy link
Contributor

🎉 This change has been released in version 8.5.0-alpha.13

@parseplatformorg parseplatformorg added the state:released-alpha Released as alpha version label Nov 23, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

state:released-alpha Released as alpha version

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Remove detailed error from error messages

3 participants